package jmars.mobile.worker;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import jmars.mobile.common.Investigator;
import jmars.mobile.common.Sha;
import jmars.mobile.datainvestigation.Summary;

/**
 * group = 1,2
 * @author Jan
 *
 */
public class EntropyTrafficTotalHourly implements Investigator {
	private String computeAll(File[] files,Map<String,Double> entropiesWorkday,Map<String,Double> entropiesWeekend,Map<String,Map<Integer,Double>> propertiesWorkday,Map<String,Map<Integer,Double>> propertiesWeekend) throws IOException{
		Long total = 0L;
		Long totalRecord = 0L,validRecord=0L,workdayRecord=0L,weekendRecord=0L;
		Long line;
		int filesFinished=0;
		FileInputStream fis;
		BufferedReader reader;
		String row;
		String[] cols;
		Map<String,Double> groupCountsWorkday=new HashMap<String,Double>();
		Map<String,Double> groupCountsWeekend=new HashMap<String,Double>();
		String groupKey = null;
		Calendar cal=Calendar.getInstance();
		Date beginDate=null;
		Integer hour,day;
		DecimalFormat df = new DecimalFormat();
		df.applyPattern("0.000");
		Double p;
		Double h;
		for(File inputFile:files){
			fis = new FileInputStream(inputFile);
			reader = new BufferedReader(new InputStreamReader(fis));
			line=0L;
			while((row=reader.readLine())!=null){
				line++;
				totalRecord++;
				try{
					cols=row.split(Summary.columnSeperator);
					if(Summary.filterRow(cols,true,false)) continue;
					beginDate=Summary.parseBeginDate(cols);
					if(beginDate==null) continue;
					cal.setTime(beginDate);
					day=cal.get(Calendar.DAY_OF_MONTH);
					if(day<17||day>22) continue;
					validRecord++;
//					if(beginDate.getTime()<1353110400000L||beginDate.getTime()>1353628800000L) continue;
					groupKey=Summary.genGroupKey(cols);
					hour=cal.get(Calendar.HOUR_OF_DAY);
					if(cal.get(Calendar.DAY_OF_WEEK)==Calendar.SUNDAY||cal.get(Calendar.DAY_OF_WEEK)==Calendar.SATURDAY){
						weekendRecord++;
						if(propertiesWeekend.containsKey(groupKey)){
							if(propertiesWeekend.get(groupKey).containsKey(hour)){
								propertiesWeekend.get(groupKey).put(hour, propertiesWeekend.get(groupKey).get(hour)+Double.parseDouble(cols[10]));
							}else{
								propertiesWeekend.get(groupKey).put(hour, Double.parseDouble(cols[10]));
							}
							groupCountsWeekend.put(groupKey, groupCountsWeekend.get(groupKey)+Double.parseDouble(cols[10]));
						}else{
							propertiesWeekend.put(groupKey, new HashMap<Integer,Double>());
							propertiesWeekend.get(groupKey).put(hour, Double.parseDouble(cols[10]));
							groupCountsWeekend.put(groupKey,Double.parseDouble(cols[10]));
							entropiesWeekend.put(groupKey, 0D);
							total++;
						}
					}else{
						workdayRecord++;
						if(propertiesWorkday.containsKey(groupKey)){
							if(propertiesWorkday.get(groupKey).containsKey(hour)){
								propertiesWorkday.get(groupKey).put(hour, propertiesWorkday.get(groupKey).get(hour)+Double.parseDouble(cols[10]));
							}else{
								propertiesWorkday.get(groupKey).put(hour, Double.parseDouble(cols[10]));
							}
							groupCountsWorkday.put(groupKey, groupCountsWorkday.get(groupKey)+Double.parseDouble(cols[10]));
						}else{
							propertiesWorkday.put(groupKey, new HashMap<Integer,Double>());
							propertiesWorkday.get(groupKey).put(hour, Double.parseDouble(cols[10]));
							groupCountsWorkday.put(groupKey,Double.parseDouble(cols[10]));
							entropiesWorkday.put(groupKey, 0D);
							total++;
						}
					}
				}catch(Exception ex){
					System.out.println("exception at line "+line+" of "+inputFile.getName());
					System.out.println(row);
					ex.printStackTrace();
					if(!Summary.errorResume){
						System.exit(0);
					}
				}
			}
			filesFinished++;
			reader.close();
			fis.close();
			System.out.println(filesFinished+": "+inputFile.getName()+" finished at "+line);
		}
		for(String k:propertiesWorkday.keySet()){
			h=0D;
			for(Integer kk:propertiesWorkday.get(k).keySet()){
				p=propertiesWorkday.get(k).get(kk)/groupCountsWorkday.get(k);
				h=h-p*Math.log(p)/Math.log(2);
			}
			entropiesWorkday.put(k, h);
		}
		for(String k:propertiesWeekend.keySet()){
			h=0D;
			for(Integer kk:propertiesWeekend.get(k).keySet()){
				p=propertiesWeekend.get(k).get(kk)/groupCountsWeekend.get(k);
				h=h-p*Math.log(p)/Math.log(2);
			}
			entropiesWeekend.put(k, h);
		}
		return "TotalRecord:"+totalRecord+",ValidRecord:"+validRecord+",workdayRecord:"+workdayRecord+",weekendRecord:"+weekendRecord;
	}
	
	@Override
	public Long compute(File[] files, FileWriter writer) throws IOException {
		Long total = 0L;
		Map<String,Double> entropiesWorkday=new HashMap<String,Double>();
		Map<String,Double> entropiesWeekend=new HashMap<String,Double>();
		Map<String,Map<Integer,Double>> propertiesWorkday=new HashMap<String,Map<Integer,Double>>();
		Map<String,Map<Integer,Double>> propertiesWeekend=new HashMap<String,Map<Integer,Double>>();
		writer.write("%"+computeAll(files,entropiesWorkday,entropiesWeekend,propertiesWorkday,propertiesWeekend)+"\r\n");
		DecimalFormat df = new DecimalFormat();
		df.applyPattern("0.000");
		writer.write("%"+Summary.groupColumns+"(workday): "+entropiesWorkday.size()+"\r\n");
		writer.write("%"+Summary.groupColumns+"(weekend): "+entropiesWeekend.size()+"\r\n");
		ArrayList<Entry<String,Double>> l = new ArrayList<Entry<String,Double>>(entropiesWorkday.entrySet());    

        Collections.sort(l, new Comparator<Map.Entry<String, Double>>() {
            public int compare(Map.Entry<String, Double> o1, Map.Entry<String, Double> o2) {
                return o2.getValue()==o1.getValue()?0:(o2.getValue()>o1.getValue()?1:-1);
            }
        });

		writer.write("%*****************workday entropy******************\r\n%");
		for(Integer idx:Summary.groupColumnIdx){
			writer.write(Summary.columns[idx]+",");
		}
		writer.write("workday_hourly_traffic_entropy\r\n");
        for(Entry<String,Double> e : l) {
            writer.write(Sha.digest32HexSHA1(e.getKey())+Sha.getBASE64(e.getKey())+","+e.getValue());
            for(int t=0;t<=23;t++){
            	writer.write(",");
            	if(propertiesWorkday.get(e.getKey()).containsKey(t))
            		writer.write(Summary.intFormat.format(propertiesWorkday.get(e.getKey()).get(t)));
            	else
            		writer.write("0");
            }
            writer.write("\r\n");
        }

		l = new ArrayList<Entry<String,Double>>(entropiesWeekend.entrySet());

        Collections.sort(l, new Comparator<Map.Entry<String, Double>>() {
            public int compare(Map.Entry<String, Double> o1, Map.Entry<String, Double> o2) {
                return o2.getValue()==o1.getValue()?0:(o2.getValue()>o1.getValue()?1:-1);
            }
        });

		writer.write("%*****************weekend entropy******************\r\n%");
		for(Integer idx:Summary.groupColumnIdx){
			writer.write(Summary.columns[idx]+",");
		}
		writer.write("weekend_hourly_traffic_entropy\r\n");
        for(Entry<String,Double> e : l) {
            writer.write(Sha.digest32HexSHA1(e.getKey())+Sha.getBASE64(e.getKey())+","+e.getValue());
            for(int t=0;t<=23;t++){
            	writer.write(",");
            	if(propertiesWeekend.get(e.getKey()).containsKey(t))
            		writer.write(Summary.intFormat.format(propertiesWeekend.get(e.getKey()).get(t)));
            	else
            		writer.write("0");
            }
            writer.write("\r\n");
        }
        
		return total;
	}

}
